"""
 针对不同的设备生成的报告信息做对应的处理,前端通过websocket进行数据交互
 根据患者的体检号获取到对应的报告信息
"""
import asyncio
import base64
import json
import logging
import os
import threading
import time

import pyodbc
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer


class Report:
    def __init__(self, conf):
        self.conf = conf
        # 获取当前设备的类型信息
        self.REPORT_TYPE = conf['base']['type']
        # 是否开启监听文件夹变动
        self.OPEN_WATCH_DIR = conf['base']['open_watch_dir']
        # 需要监听的文件夹
        self.WATCH_DIR = conf['base']['watch_dir']
        # 是否支持向检测设备中插入患者信息
        self.SET_INFO = conf['base']['set_info']
        # 检查结果图片保存路径
        self.PIC_DIR = conf['pic_dir']['dir']

        # 体检号
        self.examNo = ''
        # 文件列表
        self.files = []
        self.cursor = None
        # 保存当前长链接session
        self.CONNECTION_SET = set()
        self.send_msg_loop = None  # 新开一个loop,用于在同步代码中调用异步方法
        self.logger = logging.getLogger('main')
        self.myHandler = MyHandler(report=self)

    def connect_adb(self):
        """
            数据库连接信息
        :return:
        """
        if self.conf.has_option(section='sqlserver', option=''):
            """
                如果配置了sqlserver的连接信息,则这里进行链接
            """
            driver = self.conf['sqlserver']['driver']
            host = self.conf['sqlserver']['host']
            database = self.conf['sqlserver']['database']
            username = self.conf['sqlserver']['username']
            pwd = self.conf['sqlserver']['pwd']
            conn = pyodbc.connect(f'DRIVER={driver};SERVER={host};DATABASE={database};UID={username};PWD={pwd}')
            self.cursor = conn.cursor()

        if self.conf.has_option(section='access', option='database'):
            self.logger.info('connection database...')
            global table, pre_table
            """
                如果配置了access 的连接信息,则这里进行链接
            """
            driver = self.conf['access']['driver']
            database = self.conf['access']['database']
            table = self.conf['access']['table']
            pre_table = self.conf['access']['pre_table']
            try:
                con_str = r"DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};" + f"DBQ={database};"
                print(con_str)
                conn = pyodbc.connect(con_str)
                self.cursor = conn.cursor()
                self.logger.info('connection database success')
            except Exception as e:
                self.logger.error('connection error:{}'.format(e))

    async def send_msg(self, msg):
        """
            发送消息给前端
        :param msg: 需要发送的json数据
        :return:
        """
        self.logger.info('发送消息:{}'.format(msg))
        if self.CONNECTION_SET:
            for c in self.CONNECTION_SET:
                await c.send(json.dumps(msg))

    async def select_info_by_no(self, no):
        """
            根据体检号查询报告信息
        :param no: 体检号
        :return: 报告信息,可以为None
        """
        try:
            if self.cursor:
                self.cursor.execute(f"select * from {table} where 编号='{no}'")
                r = self.cursor.fetchone()
                self.logger.info('查询到数据:{}'.format(r))
                return r
            else:
                self.logger.info('未查询到相关数据,编号:{}'.format(no))
                return ''
        except Exception as e:
            self.logger.error('查询出错:{}'.format(e))
            return ''

    async def find_pic_by_dir(self, examNo):
        """
        在指定的文件夹中获取文件列表
        :param examNo:
        :param websocket:
        :return:
        """
        examNo = str(examNo)
        self.files = os.listdir(self.PIC_DIR)
        if self.files:
            result = list(filter(lambda p: examNo in p, self.files))
            self.logger.info('查询到的文件:{}'.format(result))
            if result:
                pic = ''
                if len(result) > 1:
                    for p in result:
                        pic += ',' + os.path.join(self.PIC_DIR, p)
                    pic = pic[1:]
                else:
                    pic = self.PIC_DIR + '\\' + result[0]
                return pic
        return ''

    async def send_report_info(self, report, no):
        img = await self.find_pic_by_dir(no)
        if not img:
            return
        time.sleep(2)  # 防止前端未渲染完成
        with open(img, 'rb') as f:
            img_data = f.read()
        self.logger.info('图片:{}'.format(img))
        await self.send_msg(
            {"examNo": no, "type": self.REPORT_TYPE, "data": {"value": report[6], "result": report[7].replace('-', '').replace('+', '')},
             "pic": base64.b64encode(img_data).decode('utf-8')})

    async def insert_info_to_db(self, data):
        """
            将用户信息插入到本地检测系统的数据库中
        :param data: 用户信息
        :return:
        """

        if data and self.cursor:
            regNo = data['registNo']

            select_sql = f"select * from {pre_table} where 编号='{regNo}'"
            self.cursor.execute(select_sql)
            r = self.cursor.fetchone()
            main_res = self.select_info_by_no(regNo)
            self.logger.info("查询结果:{}".format(r))
            if not r and not main_res:
                insert_sql = (f"insert into {pre_table} ([日期],[性别],[年龄],[姓名],[编号])"
                              f"values ('{time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())}','{'男' if data['sex'] == 1 else '女'}','{data['age']}','{data['patientName']}','{regNo}')")
                self.logger.info("数据插入sql:{}".format(insert_sql))
                self.cursor.execute(insert_sql)
                self.cursor.commit()

            if main_res:
                await  self.send_report_info(main_res, regNo)

    async def watchDir(self):
        """
        监听文件夹变动情况
        :return:
        """
        self.logger.info("开启监听文件夹变动")
        event_handler = self.myHandler
        observer = Observer()
        observer.schedule(event_handler, path=self.WATCH_DIR, recursive=False)
        observer.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            observer.stop()
        observer.join()

    def watch_dir_loop(self, loop):
        """
         开启一个新线程监听文件夹变动
        :param loop:
        :return:
        """
        self.logger.info("开启文件监听loop")
        asyncio.set_event_loop(loop)
        loop.run_until_complete(asyncio.gather(self.watchDir()))

    def start(self):
        self.connect_adb()
        if self.OPEN_WATCH_DIR == '1':
            thread_loop = asyncio.new_event_loop()
            t = threading.Thread(target=self.watch_dir_loop, args=(thread_loop,))
            t.daemon = True
            t.start()


class MyHandler(FileSystemEventHandler):
    def __init__(self, report):
        self.report = report
        report.logger.info("监听器初始化")

    def on_modified(self, event):
        pass

    def on_created(self, event):
        """
            判断当前新增的文件是否为图片,如果是,则去数据库中查询最新一条数据
        :param event:
        :return:
        """
        self.report.logger.info('文件被创建:{}'.format(event))
        if event.src_path:

            type_str = event.src_path.split('.')[-1].lower()
            if type_str == 'jpeg' or type_str == 'jpg' or type_str == 'png':
                self.report.logger.info('文件被创建,当前的查询cursor:{}'.format(self.report.cursor))
                if self.report.cursor:
                    self.report.cursor.execute(f'select top 1 * from {self.report.table} order by 索引 desc')
                    r = self.report.cursor.fetchone()
                    self.report.logger.info("文件被创建,查询结果:{}".format(r))
                    if r:
                        no = r[5]  # 体检号
                        value = r[6]
                        result = r[7]  # 结果,阴性-,阳性+
                    try:
                        with open(event.src_path, 'rb') as f:
                            img_data = f.read()
                        coroutine = self.report.send_msg(
                            {"examNo": no, "type": self.report.REPORT_TYPE, "data": {"value": value, "result": result},
                             "pic": base64.b64encode(img_data).decode('utf-8')})
                        if self.report.send_msg_loop is None:
                            self.report.send_msg_loop = asyncio.new_event_loop()
                        asyncio.set_event_loop(self.report.send_msg_loop)
                        future = asyncio.gather(coroutine)
                        self.report.send_msg_loop.run_until_complete(future)
                    except Exception as e:
                        self.report.logger.error("发送消息失败:".format(e))
        self.report.logger.info("文件{}被新增".format(event.src_path))
